/**
 * 例子：
 * 
    openDB('数据库名称', '表名称').then(db => {  // 打开 / 创建 Indexed 数据 
      add(db, '表名称', { id: 1, name: '张三', age: 24 }) // 新增数据
      add(db, '表名称', { id: 2, name: '李四', age: 30 }) // 新增数据
      key(db, '表名称', 1) // 通过主键读取数据
      cursor(db, '表名称') // 通过游标读取数据
      index(db, '表名称','name', '张三') // 通过索引读取数据
      cursorAndIndex(db, '表名称', 'name', '张三') // 通过索引和游标查询记录
      update(db, '表名称', { id: 1, name: '张三', age: 25 }) // 更新数据
      delete(db, '表名称', 2) // 删除数据
      cursorAndIndexDelete(db, '表名称', 'name', '张三') // 通过索引和游标删除指定的数据
      closeDB(db) // 关闭数据库
    })
    deleteDB('数据库名称') // 删除数据库
*/

/* 封装于2021-1-7 参考于 阮一峰 技术博客 */
var database = (function () {

    var __class = function () { this.__self = this; };
    __class.prototype.getSelf = function (str) {
        return this.__self;
    }


    /* 
    * 打开数据库
    * @param {object} dbName 数据库的名字
    * @param {string} storeName 仓库名称（表名）
    * @param {string} primaryKeyName 可选参数 主键名称
    * @param {string} version 可选参数 数据库的版本
    * @return {object} 该函数会返回一个数据库实例
    */
    __class.prototype.open = function (dbName, storeName, primaryKeyName = 'id', version = 1) {
        return new Promise((resolve, reject) => {
            //  兼容浏览器
            var indexedDB =
                window.indexedDB ||
                window.mozIndexedDB ||
                window.webkitIndexedDB ||
                window.msIndexedDB
            let db
            const request = indexedDB.open(dbName, version)

            request.onsuccess = function (event) {
                db = event.target.result // 数据库对象
                //console.log('数据库打开成功')
                resolve(db)
            }

            request.onerror = function (event) {
                console.log('数据库打开报错')
                reject('数据库打开报错')
            }

            request.onupgradeneeded = function (event) {
                // 数据库创建或升级的时候会触发
                console.log('onupgradeneeded')
                db = event.target.result // 数据库对象
                var objectStore
                if (!db.objectStoreNames.contains(storeName)) {

                    //设置主键
                    objectStore = db.createObjectStore(storeName, { keyPath: primaryKeyName}) // 创建表

                    objectStore.createIndex('name', 'name', { unique: false }) // 创建索引 可以让你搜索任意字段
                    // objectStore.createIndex('address', 'address', { unique: false })
                    // objectStore.createIndex('nameAddr', ['name', 'address'], {
                    //  unique: false,
                    // })
                    // objectStore.createIndex('flag', 'flag', { unique: false })
                }
            }
        })
    }

    /**
     * 新增数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {string} data 数据
     */
    __class.prototype.add = function (db, storeName, data) {
        var request = db
            .transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式（"只读"或"读写"）
            .objectStore(storeName) // 仓库对象
            .add(data)

            return new Promise((resolve, reject) => {
                request.onsuccess = function (event) {
                    //console.log('数据写入成功')
                    resolve('数据写入成功');
                }
        
                request.onerror = function (event) {
                    //console.log('数据写入失败')
                    reject('数据写入失败');
                    throw new Error(event.target.error)
                }
            });
    }

    /**
     * 通过主键读取数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {string} key 主键值
     */
    __class.prototype.key = function (db, storeName, key) {
        return new Promise((resolve, reject) => {
            var transaction = db.transaction([storeName]) // 事务
            var objectStore = transaction.objectStore(storeName) // 仓库对象
            var request = objectStore.get(key)

            request.onerror = function (event) {
                //console.log('事务失败')
                reject('事务失败');
            }

            request.onsuccess = function (event) {
                //console.log('主键查询结果: ', request.result)
                resolve(request.result)
            }
        })
    }

    /**
     * 通过游标读取数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     */
    __class.prototype.cursor = function (db, storeName) {
        let list = []
        var store = db
            .transaction(storeName, 'readwrite') // 事务
            .objectStore(storeName) // 仓库对象
        var request = store.openCursor() // 指针对象

        return new Promise((resolve, reject) => {
            request.onsuccess = function (e) {
                var cursor = e.target.result
                if (cursor) {
                    // 必须要检查
                    list.push(cursor.value)
                    cursor.continue() // 遍历了存储对象中的所有内容
                } else {
                    //console.log('游标查询结果：', list)
                    resolve(list);
                }
            }
        })
    }

    /**
     * 通过索引读取数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {string} indexName 索引名称
     * @param {string} indexValue 索引值
     */
    __class.prototype.index = function (db, storeName, indexName, indexValue) {
        var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
        var request = store.index(indexName).get(indexValue)

        return new Promise((resolve, reject) => {
            request.onerror = function () {
                //console.log('事务失败')
                reject('事务失败');
            }
            request.onsuccess = function (e) {
                var result = e.target.result
                //console.log('索引查询结果：', result)
                resolve(result);
            }
        })
    }

    /**
     * 通过索引和游标查询记录
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {string} indexName 索引名称
     * @param {string} indexValue 索引值
     */
    __class.prototype.cursorAndIndex = function (db, storeName, indexName, indexValue) {
        let list = []
        var store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 仓库对象
        var request = store
            .index(indexName) // 索引对象
            .openCursor(IDBKeyRange.only(indexValue)) // 指针对象

        return new Promise((resolve, reject) => {
            request.onsuccess = function (e) {
                var cursor = e.target.result
                if (cursor) {
                    // 必须要检查
                    list.push(cursor.value)
                    cursor.continue() // 遍历了存储对象中的所有内容
                } else {
                    //console.log('游标索引查询结果：', list)
                    resolve(list);
                }
            }
            request.onerror = function (e) {
                reject('查询失败');
            }
        })
    }

    /**
     * 更新数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {object} data 数据
     */
    __class.prototype.update = function (db, storeName, data) {
        var request = db
            .transaction([storeName], 'readwrite') // 事务对象
            .objectStore(storeName) // 仓库对象
            .put(data)

        return new Promise((resolve, reject) => {
            request.onsuccess = function () {
                //console.log('数据更新成功')
                resolve('数据更新成功');
            }

            request.onerror = function () {
                //console.log('数据更新失败')
                reject('数据更新失败');
            }
        })
    }

    /**
     * 删除数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {object} id 主键值
     */
    __class.prototype.delete = function (db, storeName, id) {
        var request = db
            .transaction([storeName], 'readwrite')
            .objectStore(storeName)
            .delete(id)

            return new Promise((resolve, reject) => {
                request.onsuccess = function () {
                    //console.log('数据删除成功')
                    resolve('数据删除成功')
                }

                request.onerror = function () {
                    //console.log('数据删除失败')
                    reject('数据删除失败')
                }
            })
    }

    /**
     * 通过索引和游标删除指定的数据
     * @param {object} db 数据库实例
     * @param {string} storeName 仓库名称
     * @param {string} indexName 索引名
     * @param {object} indexValue 索引值
     */
    __class.prototype.cursorAndIndexDelete = function (db, storeName, indexName, indexValue) {
        var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
        var request = store
            .index(indexName) // 索引对象
            .openCursor(IDBKeyRange.only(indexValue)) // 指针对象

        return new Promise((resolve, reject) => {
            request.onsuccess = function (e) {
                var cursor = e.target.result
                var deleteRequest
                if (cursor) {
                    deleteRequest = cursor.delete() // 请求删除当前项
                    deleteRequest.onerror = function () {
                        //console.log('游标删除该记录失败')
                        reject('游标删除该记录失败');
                    }
                    deleteRequest.onsuccess = function () {
                        //console.log('游标删除该记录成功')
                        resolve('游标删除该记录成功');
                    }
                    cursor.continue()
                }
            }
            request.onerror = function (e) {
                reject('游标删除该记录失败');
            }
        })
    }

    /**
     * 关闭数据库
     * @param {object} db 数据库实例
     */
    __class.prototype.close = function (db) {
        db.close()
        console.log('数据库已关闭')
    }

    /**
     * 删除数据库
     * @param {object} dbName 数据库名称
     */
    __class.prototype.deleteDB = function (dbName) {
        console.log(dbName)
        let deleteRequest = window.indexedDB.deleteDatabase(dbName)

        return new Promise((resolve, reject) => {
            deleteRequest.onerror = function (event) {
                //console.log('删除失败')
                reject('删除失败');
            }
            deleteRequest.onsuccess = function (event) {
                //console.log('删除成功')
                resolve('删除成功');
            }
        })
    }

    return new __class();
})();